home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Crossword / Source / Crossword.m < prev    next >
Text File  |  1995-06-12  |  6KB  |  294 lines

  1. /*
  2.  
  3. File Crossword.m
  4.  
  5. A crossword is a matrix of squares.  The user can adjust the squares that are empty and filled using the mouse.  When resized, a crossword automatically adjusts the number of squares in the frame to match.  It is possible to extract the words from a crossword with the -getWords method.
  6.  
  7. */
  8.  
  9. #import <appkit/appkit.h>
  10.  
  11. #import "Crossword.h"
  12. #import "CrosswordSquare.h"
  13.  
  14.  
  15. /* ————————————————————————————————————————————————————————————————————————————  */
  16.  
  17.  
  18. #define VERSION            1
  19. #define OLDHILIGHTS        4
  20.  
  21. #define spaceWidth        (squareWidth + INTERCELLWIDTH)
  22. #define cells(n)        (((n) + 1) / spaceWidth)
  23.  
  24. static BOOL        doSquare    (int inWhite, id square, id words);
  25.  
  26.  
  27. /* ————————————————————————————————————————————————————————————————————————————  */
  28.  
  29.  
  30. @implementation Crossword
  31.  
  32. - (float) getSquareWidth    {    return squareWidth;        }
  33. - (BOOL) getFill            {    return fill;            }
  34. - (BOOL) getNumbered        {    return numbered;        }
  35. - getInspector                {    return inspector;        }
  36.  
  37. - setInspector: (id) theInspector    {    inspector = theInspector;    return self;  }
  38.  
  39.  
  40. /* ————————————————————————————————————————————————————————————————————————————  */
  41.  
  42.  
  43. /*
  44.  
  45. There are currently two different versions of the crossword object that need to be unarchived.
  46.  
  47. */
  48.  
  49. + initialize
  50. {
  51.     [Crossword setVersion: VERSION];
  52.     return self;
  53. }
  54.  
  55.  
  56. /*
  57.  
  58. The frame will have no line along its left and top edges.  A crossword matrix should be shown against an appropriate background that clarifies these edges.
  59.  
  60. */
  61.  
  62. - initFrame: (NXRect *) frameRect
  63. {
  64.     [self  initFrame: frameRect  squareWidth: DEFAULTSQUAREWIDTH];
  65.     return self;
  66. }
  67.  
  68.  
  69. - initFrame: (NXRect *) frameRect  squareWidth: (float) squareSize
  70. {
  71.     NXSize    size, between;
  72.     
  73.     squareWidth = size.width = size.height = squareSize;
  74.     between.width = between.height = INTERCELLWIDTH;
  75.     numbered = NO;
  76.     
  77.     [super  initFrame:    frameRect
  78.             mode:        NX_TRACKMODE
  79.             cellClass:    [CrosswordSquare  class]
  80.             numRows:    cells(frameRect->size.height)
  81.             numCols:    cells(frameRect->size.width) ];
  82.     
  83.     [self  setCellSize: &size];
  84.     [self  setIntercell: &between];
  85.     [self  setBackgroundGray: 0.0];
  86.     [self  setCellBackgroundGray: -1.0];
  87.     [self  changeFont: [FontManager  new]];
  88.     
  89.     return self;
  90. }
  91.  
  92.  
  93. - write: (NXTypedStream *) stream
  94. {
  95.     [super  write: stream];
  96.     NXWriteTypes(stream, "fc", &squareWidth, &numbered);
  97.     return self;
  98. }
  99.  
  100.  
  101. - read: (NXTypedStream *) stream
  102. {
  103.     [super  read: stream];
  104.     switch (NXTypedStreamClassVersion(stream, "Crossword"))
  105.     {
  106.         case 1:
  107.             NXReadTypes(stream, "fc", &squareWidth, &numbered);
  108.             break;
  109.             
  110.         default:
  111.             break;
  112.     }
  113.     
  114.     return self;
  115. }
  116.  
  117.  
  118. /* ————————————————————————————————————————————————————————————————————————————  */
  119.  
  120.  
  121. /*
  122.  
  123. Here are the standard view methods that take care of user interactions.  Clicking within the frame allows squares to be filled and erased.
  124.  
  125. */
  126.  
  127. - superviewSizeChanged: (NXSize *) old
  128. {
  129.     [super  superviewSizeChanged: old];
  130.     [self  renewRows: cells(frame.size.height)  cols: cells(frame.size.width)];
  131.     [inspector  update: self];
  132.     
  133.     return self;
  134. }
  135.  
  136.  
  137. - changeFont: sender
  138. {
  139.     [self  setFont: [sender  convertFont: font]];
  140.     [inspector  update: self];
  141.     
  142.     return self;
  143. }
  144.  
  145.  
  146. - print: sender
  147. {
  148.     [superview printPSCode: self];
  149.     return self;
  150. }
  151.  
  152.  
  153. - mouseDown: (NXEvent *) event
  154. {
  155.     NXPoint        location;
  156.     
  157.     location = event->location;
  158.     [self  convertPoint: &location  fromView: nil];
  159.     fill = ![[self  cellAt: location.y / spaceWidth
  160.                           : location.x / spaceWidth] isOpaque];
  161.     
  162.     [super  mouseDown: event];
  163.     
  164.     if (numbered)
  165.             [self  unnumber];            else
  166.             [inspector  update: self];
  167.     
  168.     return self;
  169. }
  170.  
  171.  
  172. - (BOOL) acceptsFirstMouse
  173. {
  174.     return NO;
  175. }
  176.  
  177.  
  178. /* ————————————————————————————————————————————————————————————————————————————  */
  179.  
  180.  
  181. - clear: (squareColor) color;
  182. {
  183.     CrosswordSquare        * square;
  184.     int                    i;
  185.     
  186.     i = [cellList  count];
  187.     while (i--)
  188.     {
  189.         square = [cellList  objectAt: i];
  190.         [[square  setColor: color]  setLetter: EMPTY];
  191.     }
  192.     
  193.     [self  display];
  194.     
  195.     return self;
  196. }
  197.  
  198.  
  199. - putLetter: (char) letter  inSquare: (id) square
  200. {
  201.     [square  setLetter: letter];
  202.     [self  drawCellInside: square];
  203.     
  204.     return self;
  205. }
  206.  
  207.  
  208. - number
  209. {
  210.     int        i, j, n;
  211.     id        square;
  212.     
  213.     for (n = 1, i = 0; i < numRows; i++)
  214.     for (j = 0; j < numCols; j++)
  215.     if (
  216.             [square = [self  cellAt: i: j]  isOpaque] &&
  217.                 (    ![[self  cellAt: (i - 1): j]  isOpaque] ||
  218.                     ![[self  cellAt: i: (j - 1)]  isOpaque] )
  219.             
  220.                             )    [square  setNumber: n++];
  221.     
  222.     numbered = YES;
  223.     [self  display];
  224.     [inspector  update: self];
  225.     
  226.     return self;
  227. }
  228.  
  229.  
  230. - unnumber
  231. {
  232.     int        i, j;
  233.     
  234.     for (i = 0; i < numRows; i++)
  235.     for (j = 0; j < numCols; j++)
  236.             [[self  cellAt: i: j]  setNumber: EMPTY];
  237.     
  238.     numbered = NO;
  239.     [self  display];
  240.     [inspector  update: self];
  241.     
  242.     return self;
  243. }
  244.  
  245.  
  246. /* ————————————————————————————————————————————————————————————————————————————  */
  247.  
  248.  
  249. - getWords
  250. {
  251.     int        i, j;
  252.     BOOL    inWhite;
  253.     id        words;
  254.     
  255.     words = [[List  alloc]  init];
  256.     
  257.     for (i = 0; i < numRows; i++)
  258.     for (inWhite = NO, j = 0; j < numCols; j++)
  259.                 inWhite = doSquare(inWhite, [self  cellAt: i: j], words);
  260.                 
  261.     for (j = 0; j < numCols; j++)
  262.     for (inWhite = NO, i = 0; i < numRows; i++)
  263.                 inWhite = doSquare(inWhite, [self  cellAt: i: j], words);
  264.     
  265.     return words;
  266. }
  267.  
  268.  
  269. @end
  270.  
  271.  
  272. /* ————————————————————————————————————————————————————————————————————————————  */
  273.  
  274.  
  275. /*
  276.  
  277. The routine below processes an individual square when extracting words.  It exists as a separate function to avoid duplication in the across and down directions.
  278.  
  279. */
  280.  
  281. static BOOL    doSquare (inWhite, square, words)
  282.  
  283. BOOL    inWhite;
  284. id        square;
  285. id        words;
  286.  
  287. {
  288.     BOOL    opaque = [square  isOpaque];
  289.     
  290.     if (!inWhite && opaque) [words  addObject: [[List  alloc]  init]];
  291.     if (opaque) [[words  lastObject]  addObject: square];
  292.     
  293.     return opaque;
  294. }